关于SharePreferences(以下简称SP)的使用,相信从刚开发Android都开始使用了,但是对于SP的原理以及SP的缺点可能很多人没有系统的认知。
首先说一下SP的结论:
- 容易因此ANR:SP不适合存储数据量很大的信息;同时JSON以及HTML最好也不用SP存储,因为特殊字符转义是非常消耗性能的。
- 全量写入:在apply或者commit的时候,会先添加信息到内存中,在开启子线程,将内存中的信息写入到磁盘中(先清空磁盘该文件的信息,在全部写入)。
- 跨进程不安全:Sp没有跨进程的锁,就算使用MODE_MULTI_PROCESS,当频繁的操作SP时,也可能会造成数据丢失。
- 在Android N 之后 不再支持MODE_WORLD_READABLE, MODE_WORLD_WRITEABLE默认(不能被其他应读写)
- commit:当数据写入内存以及写入磁盘,都完成时调用listener。有返回值。 apply: 数据写入内存成功之后,在写入磁盘的过程中,直接调用listener,并不会等待写入磁盘完成。无返回值。
问题:
- SP为什么不适合存储很大的数据量?
- apply和commit的区别是否可以从源码中获得答案
- 为什么android7.0之后不支持MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE?
- 加载磁盘数据不是开启了子线程了吗? 那为什么还会造成ANR呢?
源码分析:
context.getSharedPreferences(String string, int mode);
我们知道实际上context的真正实现类是ContextImp,所以进入到ContextImp的getSharedPreferences方法查看:
@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
......
File file;
synchronized (ContextImpl.class) {
if (mSharedPrefsPaths == null) {
//定义类型:ArrayMap<String, File> mSharedPrefsPaths;
mSharedPrefsPaths = new ArrayMap<>();
}
//从mSharedPrefsPaths中是否能够得到file文件
file = mSharedPrefsPaths.get(name);
if (file == null) {//如果文件为null
//就创建file文件
file = getSharedPreferencesPath(name);
将name,file键值对存入集合中
mSharedPrefsPaths.put(name, file);
}
}
return getSharedPreferences(file, mode);
}
从上述代码可知,获取file文件,如果不存在就创建,存在就直接使用。其中mSharedPrefsPaths的泛型时:<String, File> , 顾名思义:<文件名,文件>。
@Override
public SharedPreferences getSharedPreferences(File file, int mode) {
//重点1
checkMode(mode);
.......
SharedPreferencesImpl sp;
synchronized (ContextImpl.class) {
//获取缓存对象(或者创建缓存对象)
final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked();
//通过键file从缓存对象中获取Sp对象
sp = cache.get(file);
//如果是null,就说明缓存中还没后该文件的sp对象
if (sp == null) {
//重点2:从磁盘读取文件
sp = new SharedPreferencesImpl(file, mode);
//添加到内存中